/**********************************************************
 *
 *  Copyright (c) 2003  SeikoEpson Inc
 *  All Rights Reserved
 *
 *  File name : f_tanh.c
 *  Function  :
 *        f_sinh function returns hypublic tangent's value.
 *        This file is copied from math.lib of 
 *                  CC33 tool(CC33v40.exe + CCPA45.exe, 
 *                  math of ansilib33v25 )
 *
 *  original file's Revision  :
 *      2000/02/10    first release                 M.Igarashi
 *  Revision  :
 *      2003/ 3/18    IrumaSoft M.Takeishi   1.st design
 *
 **********************************************************/
#include <f_math.h>
#include <f_smcvals.h>

// ALGORITHM
// 1.tanh(x)= sinh(x)/cosh(x)
//

//  the architecture of float floating point
//
//   0 30         23 22                0  32 bit
//   -----------------------------------
//  | |   exponent  |  fraction         |
//   -----------------------------------
//
//  |               |                   |
//  |    8 bits     |    23 bits        |
//
//         bit    31         sign bit        (  1 bit  )
//             30 - 23      exponent part    (  8 bits )
//             22 -  0      fraction part    ( 23 bits )
//
//


float f_tanh(float sfX){

	FLT_LNG       ckarg;       /* exchange the type to check the argument   */
	long lX;
	unsigned long ulx;
	float sfX2,sfRet;
	
	F_GETW(lX,sfX);				// get low
	F_CLR_SIGN( sfX2, sfX );	// clear sign
	
	ulx = lX&0x7fffffff;	// mask sign

	// ulx > 87 ?
	// sinh( 87...) is near max value of float-type, not over-flowed.
	if( ulx > 0x42ae0000 ){	// large val 
		/* check the argument */
		 ckarg._F = sfX;
		 F_CHECK_ARG( &ckarg );                            // check NaN
		 if ( ckarg.st._LL == f_P_INF.st._LL ) {      // x = Inf 
		    return( 1.0f );
		 } else if ( ckarg.st._LL == f_N_INF.st._LL ) {  // x = -Inf
		    return( - 1.0f );
		 }

		 // if tanh( ... ) points 1,
		 if ( (lX&0x80000000) != 0 ) {
		    return( -1.0f );
		 } else {
		    return( 1.0f );
		 }
	}
	sfRet = f_sinh(sfX2);
	sfRet /= f_cosh(sfX2);
	
	if((lX&0x80000000) == 0 ){				// positive
		return sfRet;
	}else{								// negative
		return -sfRet;
	}
	
}
